/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	TaskScheduler.cpp
*
*	Comments	-	See TaskScheduler.h
*
**************************************************************************************/
#include "../Include/TaskScheduler.h"
#include "../Include/System.h"
#include "../Include/LockFreeQueue.h"
#include "../Include/ThreadWorker.h"

namespace Pulse
{
	TaskScheduler::~TaskScheduler( void )
	{
		Shutdown();
	}

	TaskScheduler::TaskScheduler( void )
		: m_numThreads( 0 ), m_numIdleThreads(0), m_bShuttingDown( FALSE )
	{
		PSX_ZeroMem( m_pThreads, sizeof(ThreadWorker*) * PSX_MaxThreads );
	}

	void TaskScheduler::WaitUntilWorkersReady( void )
	{
		while ( m_numIdleThreads != m_numThreads - 1 );
	}

	void TaskScheduler::WorkUntilDone( ThreadTask *pTask )
	{
		TaskStatus *pStatus = pTask->GetTaskStatus();
	
		// Use the main thread to work on this also
		ThreadWorker *pWorker = m_pThreads[0];

		while ( pStatus->IsBusy())
			pWorker->DoWork( pStatus );
	}

	void TaskScheduler::AddTask( ThreadTask *pNewTask )
	{
		if ( m_pushIndex >= m_numThreads )
			m_pushIndex = 1;

		m_pThreads[m_pushIndex]->PushTask( pNewTask );
		++m_pushIndex;

		WakeWorkers();
	}

	void TaskScheduler::SignalIdle( void )
	{
		if ( m_numIdleThreads < m_numThreads - 1 )
			++m_numIdleThreads;
	}

	void TaskScheduler::WaitUntilResume( void )
	{
		m_pResumeSignal->WaitForSignaled();
		
		if ( m_numIdleThreads > 2 )
			--m_numIdleThreads;
	}

	ThreadWorker * TaskScheduler::GetThreadWorker( INDEX_T index )
	{
		return m_pThreads[index];
	}

	EErrorCode::Type TaskScheduler::Initialize( void )
	{
		// Get number of threads
		String threadName;
		
		// Set data info
		m_numThreads = System::GetNumHardwareThreads();
		m_pResumeSignal = Semaphore::Create( 0, (UINT)m_numThreads - 1 );

		// Create first thread attached to this thread
		m_pThreads[0] = new ThreadWorker();
		m_pThreads[0]->Initialize( this, PSX_String("ThreadWorker 0"), TRUE );

		for ( SIZE_T i = 1; i < m_numThreads; ++i )
		{
			threadName.Format( PSX_String("ThreadWorker %d"), i );
			m_pThreads[i] = new ThreadWorker();
			m_pThreads[i]->Initialize( this, threadName.GetBuffer() );
		}

		WaitUntilWorkersReady();

		return EErrorCode::OKAY;
	}

	void TaskScheduler::Shutdown( void )
	{
		m_bShuttingDown = TRUE;

		// Wake idle threads so that they can check to 
		//	terminate themselves.
		WakeWorkers();

		// Wait until all workers are done
		for ( SIZE_T i = 1; i < m_numThreads; ++i )
			m_pThreads[i]->Join();
	}

	void TaskScheduler::WakeWorkers( void )
	{
		m_pResumeSignal->Release( (UINT)m_numThreads - 1 );
	}
}